#ifndef DATE_H
#define DATE_H

#include <ed_core/global/defs.h>
#include <ed_core/global/library.h>

#include <QtCore/QObject>
#include <QtCore/QString>

namespace ed_core
{

/**
 * @brief The DateTime class provides Date & time facility as described by the Throalic Calendar RDM p.17.
 */
class ED_CORE_API DateTime: public QObject
{
  friend class EdCore;

  Q_OBJECT

  /**
    * @brief The number of seconds elapsed since timestamp 0 (epoch).
    * @note The DateTime associated to the epoch time is 1/1/0 TH 00:00:00.
    */
  Q_PROPERTY(quint64 timestamp READ timestamp WRITE setTimestamp)

  /**
    * @brief The year of the throalic calendar this DateTime corresponds to.
    */
  Q_PROPERTY(quint32 year READ year)

  /**
    * @brief The month of the year. This value is between 1 and 13 inclusive.
    * @note The mid-year period is considered as the 7th month. It can only have 5 days and the second part of the year starts withs month #8.
    * @see monthName
    */
  Q_PROPERTY(quint32 month READ month)

  /**
    * @brief The day of the month. This value is between 1 and 30 inclusives for months 1 to 6 and 8 to 13. It is between 1 and 5 inclusive for month 7 (mid-year).
    */
  Q_PROPERTY(quint32 day READ day)

  /**
    * @brief The hour of the day. This value is between 0 and 23 inclusive.
    */
  Q_PROPERTY(quint32 hours READ hours)

  /**
    * @brief The minutes in the hour. This value is between 0 and 59 inclusive.
    */
  Q_PROPERTY(quint32 minutes READ minutes)

  /**
    * @brief The seconds of a minute. This value is between 0 and 59 inclusive.
    */
  Q_PROPERTY(quint32 seconds READ seconds)

public:
  /**
   * @brief Returns the name of a month given its number.
   * @param month The month. This value must be between 1 and 13 inclusive.
   *
   * Below are the list of month names with their numbers, as defined per RdM p.17:
   *
   * @li Strassa (1)
   * @li Veltom (2)
   * @li Charassa (3)
   * @li Rua (4)
   * @li Mawag (5)
   * @li Gahmil (6)
   * @li Mid-year (7). see month() for details about the mid-year.
   * @li Raquas (8)
   * @li Sollus (9)
   * @li Riag (10)
   * @li Teayu (11)
   * @li Borrum (12)
   * @li Doddul (13)
   */
  static QString monthName(quint32 month);

  /**
   * @brief Builds a DateTime object, specifying the date as a number of seconds after the epoch (01/01/00 TH 00:00:00)
   * @param timestamp The number of seconds after the epoch this DateTime is.
   * @param parent @see QObject::parent()
   */
  explicit DateTime(quint64 timestamp, QObject* parent = 0);

  /**
   * @brief Builds a date by specifying the year, month, day, hours, minutes and seconds.
   * @param year The year of the Throalic calendar.
   * @param month The month of the year. Must be between 1 and 13 inclusive (see monthName() for details)
   * @param day The day of the month. Must be between 1 and 30 (or 5 for the mid year) inclusive.
   * @param hours The hour of the day. Must be between 0 and 23 inclusive.
   * @param minutes The minutes in the hour. Must be between 0 and 60 inclusive.
   * @param seconds The seconds of a minute. Must be between 0 and 60 inclusive.
   * @param parent See QObject::parent()
   */
  explicit DateTime(quint32 year=0, quint32 month=0, quint32 day=0, quint32 hours=0, quint32 minutes=0, quint32 seconds=0, QObject* parent = 0);
  virtual ~DateTime();

  /**
   * @brief see timestamp
   */
  quint64  timestamp() const;

  /**
   * @brief see seconds
   */
  quint32  seconds() const;

  /**
   * @brief see minutes
   */
  quint32  minutes() const;

  /**
   * @brief see hours
   */
  quint32  hours() const;

  /**
   * @brief see day
   */
  quint32  day() const;

  /**
   * @brief see month
   */
  quint32  month() const;

  /**
   * @brief see year
   */
  quint32  year() const;

  /**
   * @return The display name for the month stored by this DateTime.
   */
  QString monthName() const;

  /**
   * @brief Converts the date to a human readable form whose formatting is defined by the formatString argument.
   * @param formatString Defines the formatting of the returned date. It is a string composed of special tokens which are replaced by the date elements. Where:
   *
   * @li YYYY is replaced by year().
   * @li MM is replaced by month().
   * @li MMM is replaced by monthName().
   * @li DD is replaced by day().
   * @li hh is replaced by hours().
   * @li mm is replaced by minutes().
   * @li ss is replaced by seconds().
   *
   * @see shortName() and longName() for examples of formatting strings.
   */
  QString toString(const QString& formatString) const;

  /**
   * @brief Returns the date in the format MM/DD/YYYYTH hh:mm:ss
   * @see toString() for details.
   */
  QString shortName() const { return toString("MM/DD/YYYYTH hh:mm:ss"); }

  /**
   * @brief Returns the date in thr format DD MMM YYYYTH hh:mm:ss
   * @see toString() for details.
   */
  QString longName() const { return toString("DD MMM YYYYTH hh:mm:ss"); }

  /**
   * @brief A DateTime is inferior to another if its timestamp is inferior.
   */
  bool operator<(const ed_core::DateTime& other);

  /**
   * @brief A DateTime is equal to another if their timestamps are equal.
   */
  bool operator==(const ed_core::DateTime& other);

public Q_SLOTS:
  /**
   * @see timestamp
   * @note This method emits the dateChanged() signal.
   */
  void setTimestamp(quint64 t);

  /**
   * @brief Sets the timestamp by passing down the year,month,day,hours,minutes and seconds instead of the full timestamp.
   * This method checks for the validity of its input parameters and build a timestamp corresponding to the parameters.
   *
   * @param year The year of the Throalic calendar.
   * @param month The month of the year. Must be between 1 and 13 inclusive (see monthName() for details)
   * @param day The day of the month. Must be between 1 and 30 (or 5 for the mid year) inclusive.
   * @param hours The hour of the day. Must be between 0 and 23 inclusive.
   * @param minutes The minutes in the hour. Must be between 0 and 60 inclusive.
   * @param seconds The seconds of a minute. Must be between 0 and 60 inclusive.
   *
   * @note This method emits the dateChanged() signal.
   */
  void setDate(quint32 year, quint32 month, quint32 day, quint32 hours, quint32 minutes, quint32 seconds);

Q_SIGNALS:
  /**
   * @brief This signal is triggered when the timestamp has been changed, either by setDate() or setTimestamp().
   */
  void dateChanged();

private:
  quint64 _timestamp;

  quint64 monthSecs() const;
  quint64 daysSecs() const;
  quint64 hoursSecs() const;
  quint64 minutesSecs() const;
  quint64 secondsSecs() const;
};

} // namespace ed_core

#endif // DATE_H
